Skip to content

fix(transport): flush stale input on recovery reset#21

Merged
CameronBrooks11 merged 1 commit into
mainfrom
fix/recovery-reset-flush
Jun 28, 2026
Merged

fix(transport): flush stale input on recovery reset#21
CameronBrooks11 merged 1 commit into
mainfrom
fix/recovery-reset-flush

Conversation

@CameronBrooks11

Copy link
Copy Markdown
Member

Problem (found during hardware bring-up, #4)

First real-hardware session (RAMPS 1.4 / Marlin bugfix-2.1.x, no 12V) validated connect, M115 negotiation, reliable framed send, streaming, and pause/resume cleanly. The e-stop recovery path failed:

> M112
  sent M112; is_halted: True
  recovering via DTR reset + reconnect...
  < echo:M112 Shutdown
  < Error:Printer halted. kill() called!
  !! recovery failed: Printer halted. kill() called!

The DTR reset did reboot the board, but reset() deliberately doesn't flush, so the stale Error:Printer halted (buffered from the M112) was the first line connect() read — and _establish_ready correctly treats a fatal line as a halt, misreporting the freshly-rebooted board as halted.

Fix

Opt-in SerialTransport.reset(flush=True): after the reset edge, drop the input buffer. The stale output is already buffered (discarded); the post-reboot start arrives after the boot delay (survives). Default stays non-flushing so a normal open still matches start.

Validation

Re-ran the bring-up with reset(flush=True)6/6 phases pass on real hardware:

  recovered: is_halted=False, firmware='FIRMWARE_NAME:Marlin bugfix-2.1.x ...'

Unit regression: test_reset_with_flush_discards_stale_input_after_the_edge pins flush-after-the-edge ordering (pyserial mocked, no hardware).

After an M112 the board emits `Error:Printer halted. kill() called!`.
On the next connect that stale line was consumed by _establish_ready and
misreported as a fresh halt, so reset-and-reconnect recovery failed.

Add an opt-in `reset(flush=True)`: drop the input buffer right after the
reset edge, discarding the already-buffered stale output while the
post-reboot `start` (which arrives later) survives. Validated on a real
RAMPS 1.4 / Marlin bugfix-2.1.x board — e-stop then reset(flush=True) +
connect now recovers cleanly.

Default stays non-flushing so a normal open still matches `start`.
@CameronBrooks11
CameronBrooks11 merged commit 986cd46 into main Jun 28, 2026
5 checks passed
@CameronBrooks11
CameronBrooks11 deleted the fix/recovery-reset-flush branch June 28, 2026 00:17
CameronBrooks11 added a commit that referenced this pull request Jun 28, 2026
Establishes the mock/hardware separation for the test suite:

- Default suite stays 100% hardware-free. New conformance corpus
  (tests/conformance/*.trace) holds sessions captured from a real RAMPS
  1.4 / Marlin bugfix-2.1.x board, replayed via FakeTransport.from_trace
  in test_conformance.py — pinning connect/M115-negotiation and streaming
  against genuine wire output with no board attached.
- New opt-in hardware lane (tests/hardware/, @pytest.mark.hardware) runs
  live connect/query/stream/e-stop-recovery against a real controller.
  Gated in conftest.py: collected but skipped unless --port is given, so
  CI and offline devs never need hardware. Run with:
      uv run pytest tests/hardware --port /dev/ttyACM0

Validated: default run 110 passed / 4 hardware skipped; the live lane
passes 4/4 against the real board (incl. the #21 e-stop recovery).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant